package com.xlabs.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Widget;

import java.util.HashMap;
import java.util.Map;

/**
 * Chromeless Player API
 * 
 * @author Uwe Maurer
 */
public class ChromelessPlayer extends Widget
{

	/**
	 * Player state.
	 */
	public enum PlayerState
	{
		UNSTARTED(-1), ENDED(0), PLAYING(1), PAUSED(2), BUFFERING(3), CUED(5);
		
		/**
		 * state.
		 */
		private final int state;
		
		/**
		 * Instantiates an instance of {@link PlayerState}.
		 * @param state state of player.
		 */
		private PlayerState(int state)
		{
			this.state = state;
		}
		
		/**
		 * See {@link Enum}{@link #toString()}
		 */
		public String toString()
		{
			return state + "";
		}
	}

	/**
	 * Listener interface for youtube player.
	 */
	public interface PlayerListener
	{
		/**
		 * Called on video ready.
		 */
		public void onReady();

		/**
		 * Called on player's state change.
		 * 
		 * @param state
		 *            the new state.
		 */
		public void onStateChange(PlayerState state);
	}

	/**
	 * The java script object of the player.
	 */
	public static class YTPlayer extends JavaScriptObject
	{
		/**
		 * Initializes an instance of YTPlayer .
		 */
		protected YTPlayer()
		{
		}

		/**
		 * loads a video by id.
		 * 
		 * @param videoId
		 *            videoId.
		 * @param startSeconds
		 *            second to start from.
		 */
		public native final void loadVideoById(String videoId, int startSeconds) 
		/*-{
			this.loadVideoById(videoId, startSeconds);
		}-*/;

		/**
		 * Sets the size of the player.
		 * 
		 * @param width
		 *            Width in pixels.
		 * @param height
		 *            Height in pixels.
		 */
		public native final void setSize(int width, int height)
		/*-{
			this.setSize(width, height);
		}-*/;

		/**
		 * Sets the volume of the player.
		 * 
		 * @param volume
		 *            The volume of the player.
		 */
		public native final void setVolume(int volume)
		/*-{
			this.setVolume(volume);
		}-*/;

		/**
		 * Pauses the video.
		 */
		public native final void pauseVideo()
		/*-{
			this.pauseVideo();
		}-*/;

		/**
		 * Pauses the video.
		 */
		public native final void playVideo()
		/*-{
			this.playVideo();
		}-*/;

		/**
		 * Returns the current time of the video.
		 * 
		 * @return time of the video in seconds.
		 */
		public native final double getTime() 
		/*-{
			return this.getCurrentTime();
		}-*/;

		public native final void seekTime(int seconds)
		/*-{
			this.seekTo(seconds, true);
		}-*/;
	}

	/**
	 * Holds a mapping from player names to actual player objects.
	 */
	private static final Map<String, ChromelessPlayer> map = new HashMap<String, ChromelessPlayer>();

	static
	{
		register();
	}

	private static native void register() 
	/*-{
		$wnd.onYouTubePlayerReady = function(playerId) 
		{
		      var ytplayer = $wnd.document.getElementById(playerId + "api");
		      @com.xlabs.client.ChromelessPlayer::onReady(Ljava/lang/String;Lcom/xlabs/client/ChromelessPlayer$YTPlayer;)(playerId, ytplayer);
		      var name = 'onPlayerStateChange' +playerId;
		      $wnd[name]  = function(state) {
		              @com.xlabs.client.ChromelessPlayer::onStateChange(Ljava/lang/String;I)(playerId, state);
		      }
		      ytplayer.addEventListener("onStateChange", name);
		  }
	}-*/;

	@SuppressWarnings("unused")
	private static void onReady(String playerId, YTPlayer playerElement)
	{
		ChromelessPlayer player = map.get(playerId);
		if (player != null)
		{
			player.player = playerElement;
			if (player.listener != null)
			{
				player.listener.onReady();
			}
		}
	}

	@SuppressWarnings("unused")
	private static void onStateChange(String playerId, int state)
	{
		ChromelessPlayer player = map.get(playerId);

		if (player != null && player.listener != null && state >= -1)
		{
			player.listener.onStateChange(PlayerState.values()[state]);
		}
	}

	/**
	 * Name of this player.
	 */
	private final String name;

	/**
	 * The player element.
	 */
	private YTPlayer player;

	/**
	 * Listener for player's state changes.
	 */
	private PlayerListener listener;

	/**
	 * Current video id.
	 */
	private String currentVideoId;

	/**
	 * Initializes an instance of ChromelessPlayer.
	 * 
	 * @param name
	 */
	public ChromelessPlayer(String name)
	{
		Element elem = DOM.createDiv();
		elem.setId(name);
		setElement(elem);
		this.name = name;
		map.put(name, this);
	}

	public String getCurrentVideoId()
	{
		return this.currentVideoId;
	}

	/**
	 * Registers the listender and creates the swf object that plays the video.
	 * 
	 * @param width
	 *            Width of the video object.
	 * @param height
	 *            Height of the vidoe object.
	 * @param listener
	 *            Listener to be told about swf object state changes.
	 */
	public void createSwf(int width, int height, PlayerListener listener)
	{
		this.listener = listener;
		createSwf(name, width, height);
	}

	/**
	 * Creates the actual swf object.
	 * 
	 * @param width
	 *            Width of the video object.
	 * @param height
	 *            Height of the vidoe object.
	 */
	private native void createSwf(String name, int width, int height) /*-{
		var params = { allowScriptAccess: "always", bgcolor: "#000000" };
		var atts = { id: name + "api" };
		$wnd.swfobject.embedSWF("http://www.youtube.com/v/JPyuH4qXLZ0?enablejsapi=1&playerapiid=" + name,
		   name, ""+width, ""+height, "8", null, null, params, atts);
	}-*/;

	/**
	 * Loads a video by id.
	 * 
	 * @param videoId
	 *            The id of the video.
	 * @param startSeconds
	 *            Second to start from.
	 */
	public void loadVideoById(String videoId, int startSeconds)
	{
		this.currentVideoId = videoId;
		if (player != null)
		{
			player.loadVideoById(videoId, startSeconds);
		}
	}

	/**
	 * Pauses the video.
	 */
	public void pauseVideo()
	{
		if (player != null)
		{
			player.pauseVideo();
		}
	}

	/**
	 * Plays the loaded video.
	 */
	public void playVideo()
	{
		if (player != null)
		{
			player.playVideo();
		}
	}

	/**
	 * Seeks the video to the given time.
	 * 
	 * @param time
	 *            time to play.
	 */
	public void seekTime(double time)
	{
		if (player != null)
		{
			player.seekTime((int) time);
		}
	}

	/**
	 * returns the current time of the video.
	 * 
	 * @return time of the video.
	 */
	public double getTime()
	{
		if (player != null)
		{
			return player.getTime();
		}

		return -1;
	}

	/**
	 * Sets the size of the player object.
	 * 
	 * @param width
	 *            width in pixel.
	 * @param height
	 *            height in pixel.
	 */
	public void setSize(int width, int height)
	{
		if (player != null)
		{
			player.setSize(width, height);
		}
	}

	/**
	 * Sets the volume of the player.
	 * 
	 * @param volume
	 *            target volume.
	 */
	public void setVolume(int volume)
	{
		if (player != null)
		{
			player.setVolume(volume);
		}
	}

	/**
	 * Gets the element object of the player.
	 * 
	 * @return player's element.
	 */
	public Element getPlayer()
	{
		return player.cast();
	}

}
